#pragma once 

#include <queue>
#include <pthread.h>

using namespace std;

template<typename T>
class BlockQueue
{
private:
    int _cap;
    pthread_mutex_t _mutex;
    pthread_cond_t _Pcond;
    pthread_cond_t _Ccond;
    queue<T> _q;
private:
    void LockQueue()
    {
        pthread_mutex_lock(&_mutex);
    }
    void UnlockQueue()
    {
        pthread_mutex_unlock(&_mutex);
    }
    bool isFull()
    {
        return _cap == _q.size();
    }
    void proBlockWait()
    {
        pthread_cond_wait(&_Pcond, &_mutex);
    }
    void conBlockWait()
    {
        pthread_cond_wait(&_Ccond, &_mutex);
    }
    void WakeupPro()
    {
        pthread_cond_signal(&_Pcond);
    }
    void WakeupCon()
    {
        pthread_cond_signal(&_Ccond);
    }
    bool isEmpty()
    {
        return _q.empty();
    }
public:
    BlockQueue(int cap = 5)
        :_cap(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_Pcond, nullptr);
        pthread_cond_init(&_Ccond, nullptr);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_Pcond);
        pthread_cond_destroy(&_Ccond);
    }
    
    void push(const T& in) //生产接口
    {
        LockQueue();
        while (isFull())
        {
            proBlockWait();
        }
        _q.push(in);
        UnlockQueue();
        WakeupCon();
    }

    T pop()
    {
        LockQueue();
        while(isEmpty())
        {
            conBlockWait();
        }
        T tmp = _q.front(); 
        _q.pop();
        UnlockQueue();
        WakeupPro();
        return tmp;
    }
};
